package com.xzs.servlet;

import com.xzs.annaotation.*;
import com.xzs.controller.LYTController;

import java.lang.annotation.Annotation;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by zs.xu on 2020/8/13.
 */
public class XZSDispatcherServlet extends HttpServlet {
    //用于保存所有的类的全路径名
    List<String> classList = new ArrayList<String>();
    //用于保存所有的bean实例 key:注解的值,value:对象                  的 实例）
    Map<String,Object> beanMap = new HashMap<String, Object>();
    //用于保存Controller里的url对呀的方法 key:类上加方法上的RequestMapping的值,value:对呀方法
    Map<String,Object> urlMap = new HashMap<String, Object>();
    public void init(ServletConfig config){
        //1:扫描出项目下所有的class文件，并存对应类的全路径名，以便于后面用反射实例化对象。
        scanPackage("com.xzs");
        //2: 根据第一步扫描出的class，配合类对应使用的注解，映射出类对应的实现类 保存到map（key:注解的值,value:对象                  的 实例）中。
        doInstance();
        //3：把service实例注入到Controller对象中的service属性
        doIoc();
        //4：把Controller的方法与@RequestMapping的value做映射关系，并存下来
        buildUrlMapping();
    }


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //根据路径拿到对应的方法
        //1：获取请求路径--
        String requestURI = req.getRequestURI();
        String contextPath = req.getContextPath();
        String path = requestURI.replace(contextPath, "");//urlMap的key

        //2：拿到方法
        Method method = (Method)urlMap.get(path);
        String[] split = path.split("/");
        String controllerPath=null!=split&&split.length>1?"/"+split[1]:"";
        //3:拿到方法对应的控制类
        Object o = beanMap.get(controllerPath);
        //4:参数处理
        Object arg[] = hand(req,resp,method);
        try {
            method.invoke(o,arg);
        } catch (IllegalAccessException e) {


        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
    //获取方法中的参数
    private static Object[] hand(HttpServletRequest request, HttpServletResponse response,Method method){
        if (null==method){
            return null;
        }
        //拿到当前执行方法有哪些参数
        Class<?>[] paramClazzs = method.getParameterTypes();
        //根据参数个数，new一个参数的数组，将方法里的所有参数赋值大agrs中
        Object[] args = new Object[paramClazzs.length];

        int args_i = 0;
        int index = 0;
        for (Class<?>  paramClazz : paramClazzs) {
            if(ServletRequest.class.isAssignableFrom(paramClazz)){
                args[args_i++] = request;
            }
            if(ServletResponse.class.isAssignableFrom(paramClazz)){
                args[args_i++] = response;
            }

            Annotation[] paramAns = method.getParameterAnnotations()[index];
            if(paramAns.length>0){
                for (Annotation paramAn : paramAns) {
                    if(XzsRequestParam.class.isAssignableFrom(paramAn.getClass())){
                        XzsRequestParam mp = (XzsRequestParam) paramAn;
                        //找到注解里的name和value
                        args[args_i++] = request.getParameter(mp.value());
                    }
                }
            }
            index++;
        }
        return args;
    }


    //4：url 映射 把方法和请求地址做关联 ；；；/xzs/select 找到对应的方法
    private void buildUrlMapping() {
        if (beanMap.entrySet().size() <= 0){
            System.out.println("没有一个被实例化的类");
        }
        for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
            Class<?> clasz = entry.getValue().getClass();
            if (clasz.isAnnotationPresent(XzsController.class)){
                XzsRequestMapping controllerMapping = clasz.getAnnotation(XzsRequestMapping.class);
                String controllerUrl = controllerMapping.value();//拿到了---》/xzs
                Method[] methods = clasz.getMethods();
                for (Method method:methods){
                    //判断属性值上有没有注解
                    if (method.isAnnotationPresent(XzsRequestMapping.class)){
                        XzsRequestMapping methodMapping = method.getAnnotation(XzsRequestMapping.class);
                        String methodUrl = methodMapping.value();//拿到了---》/select
                        //controllerUrl+methodUrl == /xzs/select
                        //key:url路径 ,value:待执行的方法
                        urlMap.put(controllerUrl+methodUrl,method);
                    }else{
                        continue;
                    }
                }
            }
        }
    }

    //把service注入到controller控制层
    private void doIoc() {
        if (beanMap.entrySet().size() <= 0){
            System.out.println("没有一个被实例化的类");
        }

        for (Map.Entry<String, Object> entry: beanMap.entrySet()) {
            Object instance = entry.getValue();
            Class<?> clasz = instance.getClass();
            if (clasz.isAnnotationPresent(XzsController.class)){
                //@XzsAutowired("LYTService")
                //private LYTService lytService;
                Field[] fields = clasz.getDeclaredFields();
                for (Field field:fields){
                    //判断属性值上有没有注解
                    if (field.isAnnotationPresent(XzsAutowired.class)){
                        XzsAutowired annotation = field.getAnnotation(XzsAutowired.class);
                        String key = annotation.value();//"LYTService"
                        field.setAccessible(true);//private类型 的属性  注入不进去，需要放开权限
                        //设置值
                        try {
                            field.set(instance,beanMap.get(key));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }else{
                        continue;
                    }
                }
            }else if (clasz.isAnnotationPresent(XzsService.class)){
                //@XzsAutowired("LYTService")
                //private LYTService lytService;
                Field[] fields = clasz.getDeclaredFields();
                for (Field field:fields){
                    //判断属性值上有没有注解
                    if (field.isAnnotationPresent(XzsAutowired.class)){
                        XzsAutowired annotation = field.getAnnotation(XzsAutowired.class);
                        String key = annotation.value();//"LYTService"
                        field.setAccessible(true);//private类型 的属性  注入不进去，需要放开权限
                        //设置值
                        try {
                            field.set(instance,beanMap.get(key));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }else{
                        continue;
                    }
                }
            }
        }
    }

    //根据扫描出的list全类名，进行实例化
    private void doInstance() {
        if (null==classList ||classList.size()<=0){
            System.out.println("包扫描失败");
            return;
        }
        for (String className : classList){
            className = className.replace(".class","");

            try {
                Class<?> classZ = Class.forName(className);
                //判断有没有用注解
                if (classZ.isAnnotationPresent(XzsController.class)){
                    Object instance = classZ.newInstance();//创建控制类实例化对象
                    XzsRequestMapping requestMapping = classZ.getAnnotation(XzsRequestMapping.class);
                    String rmValue = requestMapping.value();
                    beanMap.put(rmValue,instance);
                }else if (classZ.isAnnotationPresent(XzsService.class)){
                    XzsService service = classZ.getAnnotation(XzsService.class);
                    Object instance = classZ.newInstance();
                    beanMap.put(service.value(),instance);
                }else{
                    continue;
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    //扫描"com.xzs"路径下文件
    private void scanPackage(String basePackage) {
        URL url = this.getClass().getClassLoader().getResource("/"+basePackage.replaceAll("\\.","/"));
        String fileStr = url.getFile();
        File file = new File(fileStr);
        String[] filesStr = file.list();
        for (String path : filesStr){
            //筛选出文件
            File filePath = new File(fileStr+path);
            if (filePath.isDirectory()){
                scanPackage( basePackage+"."+path);
            }else{
                //找到class类
                classList.add(basePackage+"."+filePath.getName());//内容是全类名路径
            }

        }
    }
}
